package link.anycar.study.java8;

import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoField;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAccessor;
import java.util.Date;

/**
 * Instant always take +00:00 as is time zone.
 * The new date time lib offers a ZoneId to handle time zone.
 * It's formatter is thread safe.
 * The most important of the lib is that it clearly defines the concept of
 *  time and date, like instant, period, date, time, timezone and duration.
 */
public class DateTimeFeature {

    public static void main(String[] args) {
        DateTimeFeature dtf = new DateTimeFeature();
        dtf.construction();
        dtf.now();
        dtf.fieldValue();
        dtf.calculation();
        dtf.betweenOldAndNew();
        dtf.clock();
        dtf.format();
    }

    private void printSeparator(int index) {
        String caller = Thread.currentThread().getStackTrace()[2].getMethodName();
        System.out.println("\n==========" + caller + ": " + index + "==========");
    }

    private void construction() {
        printSeparator(0);
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        System.out.println("Parse time: 2019-02-12 15:33:30");
        TemporalAccessor tmpl = formatter.parse("2019-02-12 15:33:30");
        LocalDateTime ldt = LocalDateTime.from(tmpl);
        ZoneOffset offset = ZoneOffset.ofHours(8);
        Instant instant = ldt.atOffset(offset).toInstant();
        ZonedDateTime zdt = ZonedDateTime.ofInstant(instant, offset);
        System.out.println("To TemporalAccessor: " + tmpl);
        System.out.println("To LocalDateTime: " + ldt);
        System.out.println("To Instant(suppose time zone is +8:00): " + instant);
        System.out.println("To ZonedDateTime(suppose +8:00): " + zdt);

        printSeparator(1);
        System.out.println("Get *Date/Time using ofXxx methods: ");
        ldt = LocalDateTime.of(2019, 2, 15, 12, 5, 25, 987000000);
        System.out.println(ldt);
    }

    private void now() {
        printSeparator(0);
        Instant instant = Instant.now();
        System.out.println(instant);

        // Local* uses local time zone, but it doesn't markdown the time zone.
        printSeparator(1);
        LocalDateTime ldt = LocalDateTime.now();
        System.out.println(ldt);

        printSeparator(2);
        LocalDate ld = LocalDate.now();
        System.out.println(ld);

        printSeparator(3);
        LocalTime lt = LocalTime.now();
        System.out.println(lt);

        printSeparator(4);
        OffsetDateTime odt = OffsetDateTime.now();
        ZonedDateTime zdt = ZonedDateTime.now();
        System.out.println(odt);
        System.out.println(odt.getOffset());
        System.out.println(odt.toInstant());
        System.out.println(zdt);
        System.out.println(zdt.getOffset());
        System.out.println(zdt.getZone());
        System.out.println(zdt.toInstant());

        printSeparator(5);
        OffsetTime ot = OffsetTime.now();
        System.out.println(ot);
        System.out.println(ot.getOffset());

        printSeparator(6);
        ZoneOffset zo = ZoneOffset.ofHours(5);
        OffsetDateTime dst = odt.toInstant().atOffset(zo);
        System.out.println(dst);
        System.out.println(dst.getOffset());

        printSeparator(7);
        YearMonth ym = YearMonth.now();
        System.out.println(ym);

        printSeparator(8);
        Year y = Year.now();
        System.out.println(y);
    }

    private void fieldValue() {
        printSeparator(0);
        LocalDateTime ldt = LocalDateTime.now();
        System.out.println(ldt);
        System.out.print("Year-> " + ldt.getYear() + ", month-> " + ldt.getMonthValue());
        System.out.print(", day-> " + ldt.getDayOfMonth() + ", hour-> " + ldt.getHour());
        System.out.print(", minute-> " + ldt.getMinute() + ", second-> " + ldt.getSecond());
        System.out.println(", nano-> " + ldt.getNano());

        printSeparator(1);
        System.out.println("Range of year-> " + ldt.range(ChronoField.YEAR));
        System.out.println("Range of month-> " + ldt.range(ChronoField.MONTH_OF_YEAR));
        System.out.println("Range of day-> " + ldt.range(ChronoField.DAY_OF_MONTH));
        System.out.println("Range of hour-> " + ldt.range(ChronoField.HOUR_OF_DAY));
        System.out.println("Range of minite-> " + ldt.range(ChronoField.MINUTE_OF_HOUR));
        System.out.println("Range of second-> " + ldt.range(ChronoField.SECOND_OF_MINUTE));
    }

    private void calculation() {
        // as the values in the new time lib are immutable, every calculation returns a completely new instance
        printSeparator(0);
        System.out.println("Change time zone from current to 10:");
        LocalDateTime ldt = LocalDateTime.now();
        ZoneOffset local = ZoneOffset.ofHours(8);
        ZoneOffset dst = ZoneOffset.ofHours(10);
        OffsetDateTime odt = ldt.toInstant(local).atOffset(dst);
        System.out.println("local: " + ldt);
        System.out.println("changed: " + odt);

        // arithmetic
        printSeparator(1);
        LocalDateTime pldt = ldt.plusHours(3);
        System.out.print(ldt + " plus 3 hours is : ");
        System.out.println(pldt);
        LocalDateTime mldt = ldt.minusMinutes(10);
        System.out.print(ldt + " minus 10 minutes is : ");
        System.out.println(mldt);
        System.out.println("One day later is: " + ldt.plusDays(1));
        LocalDate ld = LocalDate.now();
        System.out.println("One week after today is: " + ld.plusWeeks(1));
        System.out.println("Two weeks after today is: " + ld.plus(2, ChronoUnit.WEEKS));

        // comparisons
        printSeparator(2);
        Instant instant = Instant.now();
        Instant other = Instant.ofEpochMilli(System.currentTimeMillis() - 102400);
        System.out.print("Is " + instant + " after " + other + " ? ");
        System.out.println(instant.isAfter(other));

        printSeparator(3);
        Year year = Year.now();
        Year year2020 = Year.of(2020);
        System.out.println("Is " + year + " a leap year? " + year.isLeap());
        System.out.println("Is " + year2020 + " a leap year? " + year2020.isLeap());

        printSeparator(4);
        LocalDate birthDay = LocalDate.of(2002, 10, 25);
        MonthDay bmd = MonthDay.from(birthDay);
        LocalDate lm181025 = LocalDate.of(2018, 10, 25);
        MonthDay md181025 = MonthDay.from(lm181025);
        LocalDate today = LocalDate.now();
        MonthDay mdToday = MonthDay.from(today);
        System.out.println("Someone was born in " + birthDay);
        System.out.println("Is " + lm181025 + " his/her birthday? " + bmd.equals(md181025));
        System.out.println("Is today(" + today + ") his/her birday? " + bmd.equals(mdToday));

        printSeparator(5);
        YearMonth ym = YearMonth.now();
        System.out.println("Now is " + ym);
        System.out.println("This month is " + ym.getMonth());
        System.out.println("The end of this month is " + ym.atEndOfMonth());
        System.out.println("This month has " + ym.lengthOfMonth() + " days.");
        System.out.println("This year has " + ym.lengthOfYear() + " days.");

        printSeparator(6);
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime oneWeekAgo = now.minus(1, ChronoUnit.WEEKS);
        Duration duration = Duration.between(oneWeekAgo, now);
        System.out.println("One week duration: " + duration);
        System.out.println("One week duration in millis: " + duration.toMillis());
        System.out.println("One week duration in seconds: " + duration.getSeconds());
        System.out.println("One week duration in hours: " + duration.toHours());
        System.out.println("One week duration in days: " + duration.toDays());

        printSeparator(7);
        LocalDate twoWeekAgo = today.minus(2, ChronoUnit.WEEKS);
        Period period = Period.between(today, twoWeekAgo);
        System.out.println("Two week period: " + period);
        System.out.println("Two week period in days: " + period.getDays());
    }

    private void betweenOldAndNew() {
        printSeparator(0);
        System.out.println("Date -> Instant (time zone automatically calculated)");
        Date date = new Date();
        Instant instant = date.toInstant();
        System.out.println(instant);

        printSeparator(1);
        System.out.println("Instant -> Date (time zone automatically calculated)");
        System.out.println(Date.from(instant));

        printSeparator(2);
        ZoneOffset offset = ZoneOffset.ofHours(8);
        LocalDateTime ldt = LocalDateTime.ofInstant(date.toInstant(), offset);
        System.out.println("Date -> LocalDateTime(suppose time zone is +8:00)");
        System.out.println(ldt);

        printSeparator(3);
        System.out.println("LocalDateTime to Date(suppose time zone is +8:00)");
        date = Date.from(ldt.atOffset(offset).toInstant());
        System.out.println(date);
    }

    private void clock() {
        printSeparator(0);
        Clock clock = Clock.systemUTC();
        System.out.println("SystemUTC Clock is: " + clock);
        System.out.println("Time in millis is: " + clock.millis());
        System.out.println("Zone is: " + clock.getZone());
        System.out.println("Clock instant is: " + clock.instant());
        Clock clockWithDefaultZone = Clock.systemDefaultZone();
        System.out.println("Clock with default zone's instant is: " + clockWithDefaultZone.instant());
        System.out.println("Current zone time: " + clock.instant().atZone(ZoneId.systemDefault()));
    }

    private void format() {
        // DateTimeFormatter is thread safe now. Read TFSC.
        printSeparator(0);
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
        LocalDateTime ldt = LocalDateTime.now();
        System.out.println("Current time is " + ldt);
        System.out.print("Format with pattern yyyy-MM-dd HH:mm:ss.SSS -> ");

        String formatted = formatter.format(ldt);
        System.out.println(formatted);
        System.out.print("Parse " + formatted + " with pattern yyyy-MM-dd HH:mm:ss.SSS -> ");
        LocalDateTime parsedLdt = LocalDateTime.from(formatter.parse(formatted));
        System.out.println(parsedLdt);
    }

}
